package com.tj.template.application.trade;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fqgj.base.services.redis.RedisClient;
import com.fqgj.common.api.Page;
import com.fqgj.common.utils.CollectionUtils;
import com.fqgj.common.utils.DateUtil;
import com.fqgj.common.utils.StringUtils;
import com.fqgj.exception.common.ApplicationException;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.tj.bo.common.conf.config.RedisConfigUtil;
import com.tj.bo.common.conf.constant.RedisKey;
import com.tj.bo.common.conf.util.DateUtils;
import com.tj.bo.common.conf.util.SearchUtils;
import com.tj.bo.common.conf.vo.BucketVo;
import com.tj.bo.common.conf.vo.MarketTimePrice;
import com.tj.bo.product.client.ProductService;
import com.tj.bo.product.client.vo.Product;
import com.tj.bo.product.client.vo.ProductGlobalSetting;
import com.tj.bo.trade.client.ManipulateMarket;
import com.tj.bo.trade.client.SettlementOrders;
import com.tj.bo.trade.client.TradeQueryService;
import com.tj.bo.trade.client.TradeService;
import com.tj.bo.trade.client.enums.TradeTypeEnum;
import com.tj.bo.trade.client.request.BinaryTradeExtra;
import com.tj.bo.trade.client.request.CreateTradeRequestRO;
import com.tj.bo.trade.client.request.TradeByPageQueryRO;
import com.tj.bo.trade.client.response.CreateTradeResponseRO;
import com.tj.bo.trade.client.response.TradeInfoListRO;
import com.tj.bo.trade.client.response.TradeInfoRO;
import com.tj.bo.user.client.UserProductService;
import com.tj.bo.user.client.UserWanningListService;
import com.tj.template.application.enums.OrderRestErrorEnum;
import com.tj.template.application.trade.input.*;
import com.tj.template.application.trade.output.*;
import com.tj.template.application.vo.Bucket;
import com.tj.template.config.vo.RequestLocalInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

/**
 * @program: binary-option
 * @description
 * @author: binary
 * @create: 2020-06-11 09:54
 **/
@Service
public class OrderApplication {

    private static final Logger LOGGER = LoggerFactory.getLogger(OrderApplication.class);


    @Autowired
    private RedisConfigUtil redisDbConfigUtil;
    @Autowired
    private TradeService tradeService;
    @Autowired
    private TradeQueryService tradeQueryService;
    @Autowired
    private UserProductService userProductService;
    @Autowired
    private ProductService productService;
    @Autowired
    private RedisClient redisClient;
    @Autowired
    private UserWanningListService userWanningListService;
    @Autowired
    private SettlementOrders settlementOrders;
    @Autowired
    private ManipulateMarket manipulateMarket;
    private final ExecutorService executorService = Executors.newCachedThreadPool();

    /**
     * 下单
     *
     * @param request
     * @return
     */
    public CreateTradeResponse submitOrder(CreateTradeRequest request, String ip) {

        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        String version = RequestLocalInfo.getRequestBasicInfo().getAppVersion();
        Integer versionCode = RequestLocalInfo.getRequestBasicInfo().getVersionCode();
        String appCode = RequestLocalInfo.getRequestBasicInfo().getAppCode();
        String packageName = RequestLocalInfo.getRequestBasicInfo().getPackageName();

        boolean grey = userWanningListService.isGrey(userCode);
        Product product = userProductService.getProductByCode(request.getProductCode(), userCode, grey);

        ProductGlobalSetting globalSetting = productService.getGlobalSetting(grey);

        //1、校验下单参数
        this.validateTrade(userCode, request.getTradeType(), product, globalSetting, request.getAmount(), request.getOpeningTime());

        CreateTradeRequestRO createTradeRequestRO = new CreateTradeRequestRO();
        BeanUtils.copyProperties(request, createTradeRequestRO);
        createTradeRequestRO.setUserCode(userCode);
        createTradeRequestRO.setAmount(new BigDecimal(request.getAmount()));
        OpeningPriceInfo openingPrice = this.getOpeningPrice(request.getProductCode(), Long.parseLong(request.getOpeningTime()));
        if (openingPrice.getOpeningPrice().compareTo(BigDecimal.ZERO) == 0) {
            throw new ApplicationException(OrderRestErrorEnum.ORDERS_PRICE_NOT_EXIST);
        }
        createTradeRequestRO.setOpeningPrice(openingPrice.getOpeningPrice());
        createTradeRequestRO.setOpeningTime(new Date(Long.parseLong(request.getOpeningTime())));
        createTradeRequestRO.setProfit(new BigDecimal(product.getOdds()));
        createTradeRequestRO.setSettlementProfit(userProductService.getSettlementProfit(request.getProductCode(), userCode, new BigDecimal(product.getCurrentOdds()), grey));
        createTradeRequestRO.setProductPeriod(globalSetting.getOrderTimeRange().get(0).getValue());

        BinaryTradeExtra binaryTradeExtra = new BinaryTradeExtra();
        binaryTradeExtra.setUserCode(userCode);
        binaryTradeExtra.setIp(ip);
        binaryTradeExtra.setVersion(version);
        binaryTradeExtra.setVersionCode(versionCode + "");
        binaryTradeExtra.setAppCode(appCode);
        binaryTradeExtra.setPackageName(packageName);

        CreateTradeResponseRO createTradeResponseRO = tradeService.createTrade(createTradeRequestRO, binaryTradeExtra);
        //发送延迟结算消息
        //this.delayedSettlement(createTradeResponseRO.getTradeNo(), userCode, request.getTradeType());
        //放入redis有序集合
        pushInZSet(createTradeResponseRO.getTradeNo(), userCode, request.getTradeType());
        CreateTradeResponse createTradeResponse = new CreateTradeResponse();
        BeanUtils.copyProperties(createTradeResponseRO, createTradeResponse);
        return createTradeResponse;
    }

    @Async
    @Scheduled(fixedRate = 300)
    public void delayedSettlement(){
        if(LOGGER.isDebugEnabled()){
            LOGGER.info("延迟结算任务启动");
        }
        double max = System.currentTimeMillis();
        Set<Object> set = redisClient.zRangeByScore(RedisKey.BINARY_OPTION_SETTLEMENT_ORDER_KEY, 0, max);
        executorService.submit(() -> {
            try {
                for(Object o : set){
                    String value = (String) o;
                    String[] strings = value.split(",");
                    delayedSettlement(strings[0], strings[1], Integer.valueOf(strings[2]));
                }
            }catch (Exception e){
                LOGGER.warn("延迟结算任务异常:" + e);
                e.printStackTrace();
            }
        });
        int size = set != null ? set.size() : 0;
        if(size > 0){
            redisClient.zRemoveRangeByScore(RedisKey.BINARY_OPTION_SETTLEMENT_ORDER_KEY, 0, max);
        }
        if(LOGGER.isDebugEnabled()){
            LOGGER.info("延迟结算任务启动完成，本次处理任务数：" + size);
        }
    }

    /**
     * 校验下单参数有效性
     *
     * @param userCode
     * @param tradeType
     * @param product
     * @param amount
     */
    private void validateTrade(String userCode, Integer tradeType, Product product, ProductGlobalSetting globalSetting, String amount, String openingTimestamp) {

        boolean black = userWanningListService.isBlack(userCode);
        if (black) {
            throw new ApplicationException(OrderRestErrorEnum.BLACK_USER_TIPS);
        }
        if (product == null || !product.isShowFlag()) {
            LOGGER.warn("validateTrade,产品不展示,userCode:{},productCode:{}", userCode, product == null ? "" : product.getProductCode());
            throw new ApplicationException(OrderRestErrorEnum.ORDERS_PRODUCT_NOT_EXIST);
        }
        if (product.isLocked() && tradeType == TradeTypeEnum.REAL_TRADE.getCode()) {
            LOGGER.warn("validateTrade,产品加锁,userCode:{},productCode:{}", userCode, product.getProductCode());
            throw new ApplicationException(OrderRestErrorEnum.ORDERS_PRODUCT_LOCKED);
        }
        if (DateUtils.isWeekend(new Date()) && product.getProductCode().startsWith("F_")) {
            throw new ApplicationException(OrderRestErrorEnum.ORDERS_PRODUCT_INVALID);
        }
        if (StringUtils.isNotEmpty(product.getOpeningTime()) && StringUtils.isNotEmpty(product.getClosingTime())) {
            SimpleDateFormat df = new SimpleDateFormat("HH:mm");// 设置日期格式
            Date now = null;
            Date beginTime = null;
            Date endTime = null;
            try {
                now = df.parse(df.format(new Date()));
                beginTime = df.parse(product.getOpeningTime());
                endTime = df.parse(product.getClosingTime());
            } catch (Exception e) {
                LOGGER.error("============ transfer date error ===========");
            }
            if (!DateUtils.belongCalendar(now, beginTime, endTime)) {
                throw new ApplicationException(OrderRestErrorEnum.ORDERS_PRODUCT_INVALID);
            }
        }

        if (Integer.valueOf(amount) > globalSetting.getMoneyLimit().getMax()) {
            throw new ApplicationException(String.format(OrderRestErrorEnum.ORDERS_AMOUNT_UPPER.getDesc(), globalSetting.getMoneyLimit().getMax()));
        }
        if (Integer.valueOf(amount) < globalSetting.getMoneyLimit().getMin()) {
            throw new ApplicationException(String.format(OrderRestErrorEnum.ORDERS_AMOUNT_LOWER.getDesc(), globalSetting.getMoneyLimit().getMin()));
        }
        Integer openingOrders = tradeQueryService.countOpeningOrdersByUserCode(userCode, tradeType);
        Integer orderQuantityLimit = redisDbConfigUtil.getOrdersQuantityLimit();
        if (openingOrders >= orderQuantityLimit) {
            String msg = String.format(OrderRestErrorEnum.ORDERS_QUANTITY_LIMIT.getDesc(), orderQuantityLimit);
            throw new ApplicationException(msg);
        }
        //暂时延迟定为30s内允许下单
        if ((System.currentTimeMillis() - Long.parseLong(openingTimestamp)) / 1000 > redisDbConfigUtil.delaySeconds()) {
            throw new ApplicationException(OrderRestErrorEnum.ORDERS_DELAY_TOO_HIGH);
        }
    }

    private OpeningPriceInfo getOpeningPrice(String productCode, Long openingTimestamp) {

        long startTime = System.currentTimeMillis();
        long timeStart = openingTimestamp - 2000;
        long timeEnd = openingTimestamp + 2000;
        OpeningPriceInfo openingPriceInfo = new OpeningPriceInfo();
        Set<ZSetOperations.TypedTuple<Object>> openingPriceSet = redisClient.zRangeByScoreWithScores(RedisKey.ZSET_BINARY_OPTION_PRODUCT_KEY, timeStart, timeEnd, productCode);
        if (CollectionUtils.isEmpty(openingPriceSet)) {
            LOGGER.warn("============= market data in redis is empty, pls check ============");
            return openingPriceInfo;
        }

        List<Object> openingPriceStrList = this.getList(Lists.newArrayList(openingPriceSet));
        List<Long> timestampList = Lists.newArrayList();
        Map<Long, String> pricesMap = Maps.newHashMap();
        Map<Long, Long> businessTimeMap = Maps.newHashMap();
        openingPriceStrList.stream().forEach(marketData -> {
            MarketTimePrice redisMarketTimePrice = JSONObject.parseObject((String) marketData, MarketTimePrice.class);
            timestampList.add(redisMarketTimePrice.getMarketTimestamp());
            pricesMap.put(redisMarketTimePrice.getMarketTimestamp(), redisMarketTimePrice.getMarketPrice());
            businessTimeMap.put(redisMarketTimePrice.getMarketTimestamp(), redisMarketTimePrice.getBusinessTime());
        });

        Long redisOpeningTimestamp = SearchUtils.binarySearchKey(timestampList.toArray(), openingTimestamp);
        BigDecimal openingPrice = new BigDecimal(pricesMap.get(redisOpeningTimestamp));
        openingPriceInfo.setOpeningPrice(openingPrice);
        openingPriceInfo.setMarketTimestamp(redisOpeningTimestamp);
        openingPriceInfo.setBusinessTime(businessTimeMap.get(redisOpeningTimestamp));
        LOGGER.info("=================== submit order get openingPriceInfo:{} ,cost:{}==================", JSONObject.toJSONString(openingPriceInfo), System.currentTimeMillis() - startTime);
        return openingPriceInfo;
    }

    private List<Object> getList(List<ZSetOperations.TypedTuple<Object>> priceList) {
        List<Object> newList = Lists.newArrayList();
        for (int i = 0; i < priceList.size(); i++) {
            ZSetOperations.TypedTuple<Object> typedTuple = priceList.get(i);
            newList.add(typedTuple.getValue());
        }
        return newList;
    }

    private List<MarketTimePrice> transferList(List<Object> openingPriceStrList) {
        List<MarketTimePrice> marketTimePrices = Lists.newArrayList();
        openingPriceStrList.stream().forEach(marketData -> {
            MarketTimePrice redisMarketTimePrice = JSONObject.parseObject((String) marketData, MarketTimePrice.class);
            MarketTimePrice marketTimePrice = new MarketTimePrice();
            marketTimePrice.setType(redisMarketTimePrice.getType());
            marketTimePrice.setMarketPrice(redisMarketTimePrice.getMarketPrice());
            marketTimePrice.setMarketTimestamp(redisMarketTimePrice.getMarketTimestamp());
            marketTimePrices.add(marketTimePrice);
        });
        return marketTimePrices;
    }

    /**
     * 转换成行情及补齐数据
     *
     * @param openingPriceStrList
     * @return
     */
    private List<MarketTimePrice> transferListV2(List<BucketVo> openingPriceStrList) {
        List<MarketTimePrice> marketTimePrices = Lists.newArrayList();
        String prev = null;
        for (BucketVo bucketVo : openingPriceStrList) {
            boolean flag = false;
            String marketData = (String) bucketVo.getObject();
            if (marketData == null) {
                marketData = prev;
                flag = true;
            } else {
                prev = (String) bucketVo.getObject();
            }
            if (marketData == null) {
                continue;
            }
            MarketTimePrice redisMarketTimePrice = JSONObject.parseObject((String) marketData, MarketTimePrice.class);
            if (flag) {
                redisMarketTimePrice.setMarketTimestamp(bucketVo.getMarketTimestamp());
            }
            MarketTimePrice marketTimePrice = new MarketTimePrice();
            marketTimePrice.setType(redisMarketTimePrice.getType());
            marketTimePrice.setMarketPrice(redisMarketTimePrice.getMarketPrice());
            marketTimePrice.setMarketTimestamp(redisMarketTimePrice.getMarketTimestamp());
            marketTimePrice.setBusinessTime(redisMarketTimePrice.getBusinessTime());
            marketTimePrices.add(marketTimePrice);
        }
        return marketTimePrices;
    }

    /**
     * 按照时间戳顺序排
     *
     * @param marketTimePrices
     * @return
     */
    private List<MarketTimePrice> sortedMarketTimePrice(List<MarketTimePrice> marketTimePrices) {
        return marketTimePrices.stream().sorted(Comparator.comparing(MarketTimePrice::getMarketTimestamp)).collect(Collectors.toList());
    }

    /**
     * 查询订单列表
     *
     * @param request
     * @return
     */
    public TradeListResponse orderList(TradeListRequest request) {
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        Integer pageNo = request.getPageNo();
        if (pageNo == 0) {
            pageNo = 1;
        }
        Page page = new Page(15);
        page.setCurrentPage(pageNo);
        TradeByPageQueryRO tradeByPageQueryRO = new TradeByPageQueryRO()
                .setUserCode(userCode)
                .setTradeType(request.getTradeType())
                .setPage(page);
        TradeInfoListRO tradeInfoList = tradeQueryService.queryTradeInfoListByPage(tradeByPageQueryRO);

        return new TradeListResponse(tradeInfoList.getTradeInfoList(), tradeInfoList.isHasNextPage());
    }

    /**
     * 根据订单号查询订单详情
     *
     * @param request
     * @return
     */
    public TradeInfoResponse orderDetail(TradeDetailRequest request) {
        TradeInfoRO tradeInfoRO = tradeQueryService.queryTradeInfo(request.getTradeNo());
        TradeInfoResponse response = null;
        if (tradeInfoRO != null) {
            response = new TradeInfoResponse();
            BeanUtils.copyProperties(tradeInfoRO, response);
            response.setAmount(tradeInfoRO.getAmount().toString());
            response.setProfitAmount(tradeInfoRO.getProfitAmount() != null ? tradeInfoRO.getProfitAmount().toString() : "");
            response.setOpeningPrice(tradeInfoRO.getOpeningPrice().toString());
            response.setOpeningTime(DateUtil.getDateTime(tradeInfoRO.getOpeningTime()));
            response.setClosingPrice(tradeInfoRO.getClosingPrice() != null ? tradeInfoRO.getClosingPrice().toString() : "");
            response.setClosingTime(DateUtil.getDateTime(tradeInfoRO.getClosingTime()));
            response.setCreateDate(DateUtil.getDate(tradeInfoRO.getOpeningTime(), "dd/MM/yyyy HH:mm:ss"));
        }
        return response;
    }

    /**
     * 大盘数据
     *
     * @param request
     * @return
     */
    public MarketDataResponse marketData(MarketDataRequest request) {

        MarketDataResponse response = new MarketDataResponse();
        response.setRisingProbability("52%");
        response.setFallingProbability("48%");
        //180/3s 900/15s 1800/30s 3600/60s 客户端大盘X轴固定展示60个点
        long timeEnd = System.currentTimeMillis();
        long timeStart = timeEnd - request.getChartTime() * 1000;
        Set<ZSetOperations.TypedTuple<Object>> openingPriceSet = redisClient.zRangeByScoreWithScores(RedisKey.ZSET_BINARY_OPTION_PRODUCT_KEY, timeStart, timeEnd, request.getProductCode());
        Long startTime = System.currentTimeMillis();
        if (CollectionUtils.isEmpty(openingPriceSet)) {
            response.setMarketProducts(Lists.newArrayList());
        } else {
            List<ZSetOperations.TypedTuple<Object>> openingPriceStrList = new ArrayList<>(openingPriceSet);
            response.setMarketProducts(this.sortedMarketTimePrice(this.transferListV2(this.calculateList(openingPriceStrList, request.getChartTime()))));
        }
        LOGGER.info("============= get market data cost time:{} ==============", System.currentTimeMillis() - startTime);
        return response;
    }


    //生成数据，0为最新的数据
    public static void test1(int nodeNum) {

        //数据总数
        int dataSize = 36000;

        //生成的数据的频率 每隔多少毫秒产生一条数据
        int timeGapMs = 300;

        //客户端展示的数据区间 毫秒
        int showTimeRange = 180000;

        //客户端展示的节点数
        int nodeNo = 600;

        long currentTimeMillis = System.currentTimeMillis();
        Random random = new Random();
        int max = 100;
        int min = 0;

        List<MarketTimePrice> marketTimePrices = new ArrayList<>(dataSize);
        for (int i = 0; i < dataSize; i++) {
            if (i >= 1 && i <= 2) {
                //为了产生中断数据
                continue;
            }
            int s = random.nextInt(max) % (max - min + 1) + min;
            MarketTimePrice t = new MarketTimePrice()
                    .setMarketPrice(s + "")
                    .setMarketTimestamp(currentTimeMillis - i * timeGapMs)
                    .setType("" + i);

            marketTimePrices.add(t);
        }


//        test2(marketTimePrices, currentTimeMillis, showTimeRange, nodeNo);


    }

    /**
     * @param marketTimePrices 需要排序的数据集 第0个值为最新时间的数据
     * @param timeRange        每个节点的时间区间
     * @param nodeNum          最后需要区分的时间节点数
     * @return
     */
    public static Bucket[] test2(List<ZSetOperations.TypedTuple<Object>> marketTimePrices, long startTime, int timeRange, int nodeNum) {
        //多增加10个节点防止最左侧节点的第一个数据为空
        int maxNodes = nodeNum + 10;

        //最新
        //构建nodeNum个桶 当前为60，我们设定好每个桶接受的开始时间和结束时间戳 第0个为最新的时间的桶
        Bucket[] buckets = new Bucket[maxNodes];
        for (int i = 0; i < maxNodes; i++) {
            buckets[i] = new Bucket(startTime - (i + 1) * timeRange, startTime - i * timeRange);
        }

        //如果数据 0是最新的一条 size-1是最小的一条 则顺序不变，反之则数据逆序话
        int size = marketTimePrices.size();
        if (marketTimePrices.get(0).getScore() < marketTimePrices.get(size - 1).getScore()) {
//            LOGGER.info("== market数据逆序话  true==");
            Collections.reverse(marketTimePrices);
        } else {
//            LOGGER.info("== market数据逆序话  false ==");
        }

        LOGGER.info("==数据aaaabb==,marketTimePrices:{},startTime:{}", marketTimePrices.size(), startTime);
        //循环redis中数据的数组，把每条记录塞入到对应的桶内
        //j表示的是放入的当前的第几个桶了
        int j = 0;
        for (int b = 0; b < maxNodes; b++) {
            Bucket bucket = buckets[b];
            for (int m = j; m < size; m++) {
                ZSetOperations.TypedTuple<Object> marketTimePrice = marketTimePrices.get(m);
//                LOGGER.info("== 填充bucket ==,b:{},j:{},bucket:{},marketTimePrice:{}", b, j, JSON.toJSONString(bucket), JSON.toJSONString(marketTimePrice));
                if (marketTimePrice.getScore() > bucket.getStartTime() && marketTimePrice.getScore() <= bucket.getEndTime()) {
                    j++;
                    bucket.add(marketTimePrice);
                } else {
                    break;
                }
            }
        }

        Bucket bucket = buckets[buckets.length - 1];
        if (bucket.getCnt() == 0) {
            //如果左边第一个点位空，那么把右边的第一个不为空的节点不过去
            for (int i = buckets.length - 1; i >= 0; i--) {
                if (buckets[i].getCnt() != 0) {
                    buckets[buckets.length - 1] = buckets[i];
                    break;
                }
            }
        }

        //从左边开始补右边的数据
        Bucket lastAvaibbleBucket = buckets[buckets.length - 1];
        for (int i = buckets.length - 1; i >= 0; i--) {
            if (buckets[i].getCnt() == 0) {
                buckets[i].setLastBucket(lastAvaibbleBucket);
            } else {
                lastAvaibbleBucket = buckets[i];
            }
        }


        Bucket[] bucketsFinal = new Bucket[nodeNum];
        for (int i = 0; i < nodeNum; i++) {
            bucketsFinal[i] = buckets[i];
        }


        return bucketsFinal;
    }


    public static void main(String[] args) {

        JSONArray objects = JSON.parseArray("[{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952595225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952592225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952592225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952589225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952589225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952586225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952586225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952583225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952583225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952580225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952580225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952577225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952577225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952574225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952574225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952571225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952571225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952568225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952568225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952565225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952565225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952562225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952562225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952559225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952559225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952556225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952556225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952553225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952553225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952550225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952550225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952547225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952547225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952544225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952544225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952541225,\"totalPrice\":0.0},{\"avgPrice\":0.00932401,\"cnt\":1,\"endTime\":1594952541225,\"marketTimePrice\":{\"businessTime\":1410740,\"marketPrice\":\"0.00932401\",\"marketTimestamp\":1594952540000,\"type\":\"F_JPY_USD\"},\"maxPrice\":0.00932401,\"minPrice\":0.0,\"startTime\":1594952538225,\"totalPrice\":0.00932401},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952538225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952535225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952535225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952532225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952532225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952529225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952529225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952526225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952526225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952523225,\"totalPrice\":0.0},{\"avgPrice\":0.0093249,\"cnt\":1,\"endTime\":1594952523225,\"marketTimePrice\":{\"businessTime\":1410721,\"marketPrice\":\"0.0093249\",\"marketTimestamp\":1594952521000,\"type\":\"F_JPY_USD\"},\"maxPrice\":0.0093249,\"minPrice\":0.0,\"startTime\":1594952520225,\"totalPrice\":0.0093249},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952520225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952517225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952517225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952514225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952514225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952511225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952511225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952508225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952508225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952505225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952505225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952502225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952502225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952499225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952499225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952496225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952496225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952493225,\"totalPrice\":0.0},{\"avgPrice\":0.0093243,\"cnt\":1,\"endTime\":1594952493225,\"marketTimePrice\":{\"businessTime\":1410691,\"marketPrice\":\"0.0093243\",\"marketTimestamp\":1594952491000,\"type\":\"F_JPY_USD\"},\"maxPrice\":0.0093243,\"minPrice\":0.0,\"startTime\":1594952490225,\"totalPrice\":0.0093243},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952490225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952487225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952487225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952484225,\"totalPrice\":0.0},{\"avgPrice\":0.00932349,\"cnt\":1,\"endTime\":1594952484225,\"marketTimePrice\":{\"businessTime\":1410682,\"marketPrice\":\"0.00932349\",\"marketTimestamp\":1594952482000,\"type\":\"F_JPY_USD\"},\"maxPrice\":0.00932349,\"minPrice\":0.0,\"startTime\":1594952481225,\"totalPrice\":0.00932349},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952481225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952478225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952478225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952475225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952475225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952472225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952472225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952469225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952469225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952466225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952466225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952463225,\"totalPrice\":0.0},{\"avgPrice\":0.0093241,\"cnt\":1,\"endTime\":1594952463225,\"marketTimePrice\":{\"businessTime\":1410661,\"marketPrice\":\"0.0093241\",\"marketTimestamp\":1594952461000,\"type\":\"F_JPY_USD\"},\"maxPrice\":0.0093241,\"minPrice\":0.0,\"startTime\":1594952460225,\"totalPrice\":0.0093241},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952460225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952457225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952457225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952454225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952454225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952451225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952451225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952448225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952448225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952445225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952445225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952442225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952442225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952439225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952439225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952436225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952436225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952433225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952433225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952430225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952430225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952427225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952427225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952424225,\"totalPrice\":0.0},{\"avgPrice\":0.00932462,\"cnt\":1,\"endTime\":1594952424225,\"marketTimePrice\":{\"businessTime\":1410624,\"marketPrice\":\"0.00932462\",\"marketTimestamp\":1594952424000,\"type\":\"F_JPY_USD\"},\"maxPrice\":0.00932462,\"minPrice\":0.0,\"startTime\":1594952421225,\"totalPrice\":0.00932462},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952421225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952418225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952418225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952415225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952415225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952412225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952412225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952409225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952409225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952406225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952406225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952403225,\"totalPrice\":0.0},{\"avgPrice\":0.0093249,\"cnt\":1,\"endTime\":1594952403225,\"marketTimePrice\":{\"businessTime\":1410601,\"marketPrice\":\"0.0093249\",\"marketTimestamp\":1594952401000,\"type\":\"F_JPY_USD\"},\"maxPrice\":0.0093249,\"minPrice\":0.0,\"startTime\":1594952400225,\"totalPrice\":0.0093249},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952400225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952397225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952397225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952394225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952394225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952391225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952391225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952388225,\"totalPrice\":0.0},{\"avgPrice\":0.0,\"cnt\":0,\"endTime\":1594952388225,\"maxPrice\":0.0,\"minPrice\":0.0,\"startTime\":1594952385225,\"totalPrice\":0.0}]");
        List<Bucket> buckets = objects.toJavaList(Bucket.class);

//        Bucket[] buckets = new Bucket[70];


        Bucket lastAvaibbleBucket = buckets.get(buckets.size() - 1);
        for (int i = buckets.size() - 1; i >= 0; i--) {
            if (buckets.get(i).getCnt() == 0) {
                buckets.get(i).setLastBucket(lastAvaibbleBucket);
            } else {
                lastAvaibbleBucket = buckets.get(i);
            }
        }

        Bucket[] bucketsFinal = new Bucket[60];
        for (int i = 0; i < 60; i++) {
            bucketsFinal[i] = buckets.get(i);
        }

        for (int i = 0, isize = bucketsFinal.length; i < isize; i++) {
            Bucket bucket = bucketsFinal[i];
            MarketTimePrice marketTimePrice = bucket.getMarketTimePrice();

            System.err.println(i);
            if (bucketsFinal[i].getCnt() == 0) {
                marketTimePrice = bucket.getLastBucket().getMarketTimePrice();
            }


            System.err.println(11);
        }

        System.err.println(111);
    }


    /**
     * 均匀取值
     *
     * @param list
     * @param chartTime 秒
     * @return
     */
    private List<BucketVo> calculateList(List<ZSetOperations.TypedTuple<Object>> list, long chartTime) {
        if (list == null || list.size() == 0) {
            return new ArrayList<>();
        }
        
        TreeMap<Double, BucketVo> treeMap = new TreeMap<>();

        for (int i = 0; i < list.size(); i++) {
            ZSetOperations.TypedTuple<Object> typedTuple = list.get(i);
            treeMap.put(typedTuple.getScore(), new BucketVo(typedTuple.getScore().longValue(), typedTuple.getValue()));
        }

        return  new ArrayList<>(treeMap.values());
    }

    /**
     * 获取开盘中的交易列表
     *
     * @param request
     * @return
     */
    public OpeningTradeResponse openingOrders(OpeningTradeRequest request) {
        String userCode = RequestLocalInfo.getCurrentUser().getUserCode();
        OpeningTradeResponse response = new OpeningTradeResponse();
        List<TradeInfoRO> tradeInfos = tradeQueryService.queryOpeningOrdersByUserCode(userCode, request.getTradeType(), request.getProductCode());
        List<OpeningTradeResponse.OpeningTrade> openingOrders = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(tradeInfos)) {
            tradeInfos.stream().forEach(tradeInfo -> {
                OpeningTradeResponse.OpeningTrade openingTrade = new OpeningTradeResponse.OpeningTrade();
                openingTrade.setAmount(tradeInfo.getAmount().toString());
                openingTrade.setMarketPrice(tradeInfo.getAmount().toString());
                openingTrade.setMarketTimestamp(tradeInfo.getOpeningTime().getTime());
                openingTrade.setTrendType(tradeInfo.getTrendType());
                openingOrders.add(openingTrade);
            });
        }
        response.setOpeningOrders(openingOrders);
        return response;
    }

    /**
     * 轮询大盘数据
     *
     * @param request
     * @return
     */
    public MarketQueryResponse queryMarket(MarketQueryRequest request) {
        MarketQueryResponse response = new MarketQueryResponse();
        List<Object> openingPriceStrList = redisClient.listRange(RedisKey.BINARY_OPTION_PRODUCT_KEY, 0, 30, request.getProductCode());
        if (CollectionUtils.isNotEmpty(openingPriceStrList)) {
            response.setMarketProducts(Lists.newArrayList(this.transferList(openingPriceStrList).get(0)));
        }
        return response;
    }

    private void pushInZSet(String tradeNo, String userCode, Integer tradeType){
        double score = System.currentTimeMillis() + 60000;
        String value = tradeNo + ',' + userCode + ',' + tradeType;
        redisClient.zAdd(RedisKey.BINARY_OPTION_SETTLEMENT_ORDER_KEY, score, value);
    }

    /**
     * 延迟结算
     *
     * @param tradeNo
     * @param userCode
     */
    private void delayedSettlement(String tradeNo, String userCode, Integer tradeType){
        if (TradeTypeEnum.REAL_TRADE.getCode().equals(tradeType)) {
            boolean isGrey = userWanningListService.isGrey(userCode);
            LOGGER.info("debug111222,userCode:{},tradeNo:{},isGrey:{}", userCode, tradeNo, isGrey);
            if (isGrey) {
                //灰名单用户
                manipulateMarket.solve(tradeNo);
            }
        }
        settlementOrders.solve(tradeNo);
    }
//    private void delayedSettlement(String tradeNo, String userCode, Integer tradeType) {
//        if (TradeTypeEnum.REAL_TRADE.getCode().equals(tradeType)) {
//            boolean isGrey = userWanningListService.isGrey(userCode);
//            LOGGER.info("debug111222,userCode:{},tradeNo:{},isGrey:{}", userCode, tradeNo, isGrey);
//            if (isGrey) {
//                //灰名单用户
//                MessageMQ messageMQ = new MessageMQ();
//                messageMQ.setMessageId("MANIPULATE_" + tradeNo);
//                messageMQ.setMessage(tradeNo);
//                messageMQ.setTopic(MQTopicEnum.BINARY_OPTION);
//                messageMQ.setTag(MQTagEnum.MANIPULATE);
//                messageMQ.setDelay(null);
//                messageMQHandler.send(messageMQ);
//            }
//        }
//
//        MessageMQ messageMQ = new MessageMQ();
//        messageMQ.setMessageId(tradeNo);
//        messageMQ.setMessage(tradeNo);
//        messageMQ.setTopic(MQTopicEnum.BINARY_OPTION);
//        messageMQ.setTag(MQTagEnum.SETTLEMENT);
//        messageMQ.setDelay(60000L);
//        messageMQHandler.send(messageMQ);
//    }

}
